#include "parafluidbox.h"
#include "parafluidcolor.h"
#include "parafluidvector.h"
#include "parafluidsimulation.h"
#include "parafluidsimulationwidget.h"

#include <Qt>
#include <QtOpenGL>

using namespace ParaFluid;

SimulationWidget::~SimulationWidget()
{
	makeCurrent();
}


SimulationWidget::SimulationWidget(QWidget * parent)
	: QGLWidget(parent)
	, _camera(Vector(0.0, 0.0, 30.0), Orientation(0.0, 0.0, 0.0))
	, _mouseLastPosition()
	, _atomRadius(0.15)
	, _canAccessSPH(false)
{
	setFocusPolicy(Qt::StrongFocus);
	
   connect(&(Simulation::instance()), SIGNAL(stepped()), this, SLOT(redrawSPH()));
}

void SimulationWidget::redrawSPH()
{
	_canAccessSPH = true;
	updateGL();
	_canAccessSPH = false;
	Simulation::instance().nextStep();
}

void SimulationWidget::initializeGL()
{
   qglClearColor(Qt::lightGray);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glEnable(GL_LINE_SMOOTH);
}

void SimulationWidget::resizeGL(int width , int height)
{
   glViewport(0, 0, width, height);
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   
   gluPerspective(60, GLfloat(width) / GLfloat(height), 0.1, 100.0);
}

void SimulationWidget::paintGL()
{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   
	glTranslated(-_camera.position.x, -_camera.position.y, -_camera.position.z);
	glRotated(_camera.orientation.x, 1.0, 0.0, 0.0);
	glRotated(_camera.orientation.y, 0.0, 1.0, 0.0); 
	glRotated(_camera.orientation.z, 0.0, 0.0, 1.0); 
	
	if(_canAccessSPH)
	{
		class TriangleDrawer
			: public TriangleVoxelizer::ReadOnlyVisitor
		{
			
		public:
			
			void visitUnique(const Triangle * triangle)
			{
				glPushMatrix();
				glColor3f(0.0, 0.1, 0.9);
				//glBegin(GL_TRIANGLES);
				glLineWidth(2);
				glBegin(GL_LINE_LOOP);
				glVertex3f(triangle->point1().x, triangle->point1().y, triangle->point1().z);
				glVertex3f(triangle->point2().x, triangle->point2().y, triangle->point2().z);
				glVertex3f(triangle->point3().x, triangle->point3().y, triangle->point3().z);
				glEnd();
				glPopMatrix();
			}
			
		} triangleDrawer;
		
   	Simulation::instance().sph().triangleVoxelizer.acceptVisitor(triangleDrawer);
		
		class AtomDrawer
			: public AtomVoxelizer::ReadOnlyVisitor
		{
			
		private:
			
			float _radius;
			
			GLUquadric * _quadric;
			
		public:
			
			~AtomDrawer()
			{
				gluDeleteQuadric(_quadric);
			}
			
			AtomDrawer(float radius)
				: _radius(radius)
				, _quadric(gluNewQuadric())
			{
				/* does nothing */
			}
			
			void visit(const Atom & atom)
			{
				glPushMatrix();
				glTranslated(atom.currentPosition.x, atom.currentPosition.y, atom.currentPosition.z);
				
				//glColor3f(atom.substance.color.red, atom.substance.color.green, atom.substance.color.blue);
				
				float force = atom.currentForce.x + atom.currentForce.y + atom.currentForce.z;
				force /= 10;
				glColor3f(0.9, force, 0.1);
				
				
				gluSphere(_quadric, _radius, 6, 6);
				
				glPopMatrix();
			}
			
		} atomDrawer(_atomRadius);
		
   	Simulation::instance().sph().atomVoxelizer.acceptVisitor(atomDrawer);
   }
   
   glFlush();
}

void SimulationWidget::mousePressEvent(QMouseEvent * event)
{
   _mouseLastPosition = event->pos();
}

void SimulationWidget::mouseMoveEvent(QMouseEvent * event)
{
   int dx = event->x() - _mouseLastPosition.x();
   int dy = event->y() - _mouseLastPosition.y();
   
   if (event->buttons() & Qt::LeftButton) 
   {
      _camera.orientation.x += 0.1 * dy;
      _camera.orientation.y += 0.1 * dx;
   }
   else if (event->buttons() & Qt::RightButton) 
   {
      _camera.orientation.x += 0.1 * dy;
      _camera.orientation.z += 0.1 * dx;
   }
   
   _mouseLastPosition = event->pos();
   
	updateGL();
}

void SimulationWidget::keyPressEvent(QKeyEvent * event)
{
	switch (event->key()) 
	{
	case Qt::Key_O :
		_camera.position.y -= 0.1;
		break;
	case Qt::Key_L :
		_camera.position.y += 0.1;
		break;
	case Qt::Key_W :
	case Qt::Key_Up :
		_camera.position.z -= 0.1;
		break;
	case Qt::Key_S :
	case Qt::Key_Down :
		_camera.position.z += 0.1;
		break;
	case Qt::Key_A :
	case Qt::Key_Left :	
		_camera.position.x -= 0.05;
		break;
	case Qt::Key_D :
	case Qt::Key_Right :	
		_camera.position.x += 0.05;
		break;
	case Qt::Key_R :
		_camera.orientation.x = 0.0;
		_camera.orientation.y = 0.0;
		_camera.orientation.z = 0.0;
		_camera.position.x = 0.0;
		_camera.position.x = 0.0;
		_camera.position.x = 0.0;
		break;
	}
	updateGL();
}

